When decompressing an image, the Image Compression Manager performs these three major tasks:
Listing 3 provides an example of how a decompressor is chosen. The Image Compression Manager calls the CDPreDecompress function (described on CDPreDecompress ) before an image is decompressed. The decompressor returns information about how it can decompress an image. The Image Compression Manager can fit the destination pixel map to your decompressor's requirements if it is not able to support decompression to the destination directly. The capability information the decompressor returns includes
When your decompressor component is called with the CDPreDecompress function, it can handle all aspects of the call itself, or only the most common ones. All decompressors must handle at least one case.
This section contains a bulleted list of some of the operations your decompressor component can perform during the decompression operation. The list describes which parameters in the decompression parameters structure (described on The Decompression Parameters Structure ) indicate the operations are required and which flags in the flags field of the compressor capabilities structure (described on The Compressor Capability Structure ) must be set to allow your decompressor to handle them.
For sequences of images the conditionFlags field in the decompression parameters structure can be used to determine which parameters may have changed since the last decompression operation. These parameters are also indicated in the bulleted list.
Since your decompressor's capabilities depend on the full combination of parameters, it must inspect all the relevant parameters before indicating that it will perform one of the operations itself. For instance, if your decompressor has hardware that can perform scaling only if the destination pixel depth is 32 and there is no clipping, then the pre-decompression operation would have to check the following fields in the decompression parameters structure: the matrix field, the pixelSize field of the destination pixel map structure pointed to by the destPixMap field, and the maskBits fields. Only then could the decompressor decide whether to set the codecCanScale flag in the capabilities field of the decompression parameters structure.
Listing 3 Preparing for simple decompression
pascal long
CDPreDecompress(Handle storage, register CodecDecompressParams *p)
{
register CodecCapabilities*capabilities = p->capabilities;
RectdRect = p->srcRect;
/*
Check if the matrix is OK for this decompressor.
This decompressor doesn't do anything fancy.
*/
if ( !TransformRect(p->matrix,&dRect,nil) )
return(codecConditionErr);
/*
Decide which depth compressed data this decompressor can
deal with.
*/
switch ( (*p->imageDescription)->depth ) {
case 16:
break;
default:
return(codecConditionErr);
break;
}
/*
This decompressor can deal only with 32-bit pixels.
*/
capabilities->wantedPixelSize = 32;
/*
The smallest possible band the decompressor can handle is
2 scan lines.
*/
capabilities->bandMin = 2;
/* This decompressor can deal with 2 scan line high bands. */
capabilities->bandInc = 2;
/*
If this decompressor needed its pixels be aligned on
some integer multiple, you would set extendWidth and
extendHeight to the number of pixels by which you need the
destination extended. If you don't have such requirements
or if you take care of them yourself, you set extendWidth
and extendHeight to 0.
*/
capabilities->extendWidth = p->srcRect.right & 1;
capabilities->extendHeight = p->srcRect.bottom & 1;
return(noErr);
}
Listing 4 shows how to decompress the horizontal band of an image. The Image Compression Manager calls the CDBandDecompress function when it wants a decompressor to decompress an image or a horizontal band of an image. The pixel data indicated by the baseAddr field is guaranteed to conform to the criteria your decompressor specified in the CDPreDecompress function.
This example does not perform decompression on bands with a bit depth of more than one or an extension of width and height. If the example did do so, it would handle these cases faster.
Listing 4 Performing a decompression operation
pascal long
CDBandDecompress(Handle storage,register CodecDecompressParams *p)
{
Rect dRect;
long offsetH,offsetV;
Globals **glob = (Globals **)storage;
long numLines,numStrips;
short rowBytes;
long stripBytes;
short width;
register short y;
register char* baseAddr;
char *cDataPtr;
char mmuMode = 1;
OSErr result = noErr;
/*
Calculate the real base address based on the boundary
rectangle. If it's not a linear transformation, this
decompressor does not perform the operation.
*/
dRect = p->srcRect;
if ( !TransformRect(p->matrix,&dRect,nil) )
return(paramErr);
/* If there is a progress function, give it an open call at
the start of this band.
*/
if (p->progressProcRecord.progressProc)
p->progressProcRecord.progressProc(codecProgressOpen,0,
p->progressProcRecord.progressRefCon);
/*
Initialize some local variables.
*/
width = (*p->imageDescription)->width;
rowBytes = p->dstPixMap.rowBytes;
numLines = p->stopLine - p->startLine; /* number of scan lines
in this band */
numStrips = (numLines+1)>>1; /* number of strips in
this band */
stripBytes = ((width+1)>>1) * 5; /* number of bytes in
1 strip of blocks */
cDataPtr = p->data;
/*
Adjust the destination base address to be at the beginning
of the desired rectangle.
*/
offsetH = (dRect.left - p->dstPixMap.bounds.left);
switch ( p->dstPixMap.pixelSize ) {
case 32:
offsetH <<=2; /* 1 pixel = 4 bytes */
break;
case 16:
offsetH <<=1; /* 1 pixel = 2 bytes */
break;
case 8:
break; /* 1 pixel = 1 byte */
default:
result = codecErr; /* This decompressor doesn't handle
these cases, although it
could. */
goto bail;
}
offsetV = (dRect.top - p->dstPixMap.bounds.top) * rowBytes;
baseAddr = p->dstPixMap.baseAddr + offsetH + offsetV;
/*
If your decompressor component is skipping some data,
it just skips it here. You can tell because
firstBandInFrame indicates this is the first band for a new
frame, and if startLine is not 0, then that many lines were
clipped out.
*/
if ( (p->conditionFlags & codecConditionFirstBand) &&
p->startLine != 0 ) {
if ( p->dataProcRecord.dataProc ) {
for ( y=0; y < p->startLine>>1; y++ ) {
if ( (result=p->dataProcRecord.dataProc
(&cDataPtr,stripBytes,
p->dataProcRecord.dataRefCon)) != noErr ) {
result = codecSpoolErr;
goto bail;
}
cDataPtr += stripBytes;
}
} else
cDataPtr += (p->startLine>>1) * stripBytes;
}
/*
If there is a data-loading function spooling the data to your
decompressor, then you have to decompress the data in the
chunk size that is specified, or, if there is a progress
function, you must make sure to call it as you go along.
*/
if ( p->dataProcRecord.dataProc ||
p->progressProcRecord.progressProc ) {
SharedGlobals *sg = (*glob)->sharedGlob;
for (y=0; y < numStrips; y++) {
if (p->dataProcRecord.dataProc) {
if ( (result=p->dataProcRecord.dataProc
(&cDataPtr,stripBytes,
p->dataProcRecord.dataRefCon)) != noErr ) {
result = codecSpoolErr;
goto bail;
}
}
SwapMMUMode(&mmuMode);
DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
SwapMMUMode(&mmuMode);
baseAddr += rowBytes<<1;
cDataPtr += stripBytes;
if (p->progressProcRecord.progressProc) {
if ( (result=p->progressProcRecord.progressProc
(codecProgressUpdatePercent,
FixDiv(y, numStrips),
p->progressProcRecord.progressRefCon)) != noErr ) {
result = codecAbortErr;
goto bail;
}
}
}
/*
Otherwise, do the fast case.
*/
} else {
SharedGlobals *sg = (*glob)->sharedGlob;
shorttRowBytes = rowBytes<<1;
SwapMMUMode(&mmuMode);
for ( y=numStrips; y--; ) {
DecompressStrip(cDataPtr,baseAddr,rowBytes,width,sg);
baseAddr += tRowBytes;
cDataPtr += stripBytes;
}
SwapMMUMode(&mmuMode);
}
/*
IMPORTANT-- Update the pointer to data in the decompression
parameters structure, so that when your decompressor gets the
next band, you'll be at the right place in your data.
*/
p->data = cDataPtr;
if ( p->conditionFlags & codecConditionLastBand ) {
/*
Tie up any loose ends on the last band of the frame.
*/
}
bail:
/*
If there is a progress function, give it a close call
at the end of this band.
*/
if (p->progressProcRecord.progressProc)
p->progressProcRecord.progressProc(codecProgressClose,0,
p->progressProcRecord.progressRefCon);
return(result);
}